﻿/* 2012/4/18 */
using System;
using System.Collections.Generic;

namespace Cosmos.Windows.Forms.FileList
{
    /// <summary>
    /// Supervises FileList controls.
    /// </summary>
    public class FileListSupervisor : IFileList
    {
        /// <summary>
        /// The FileList controls supervised by this supervisor.
        /// </summary>
        private List<IFileList> _fileLists;

        /// <summary>
        /// Initializes a instance of FileListSupervisor class.
        /// </summary>
        public FileListSupervisor()
        {
            _fileLists = new List<IFileList>();
            Supervisee = null;
        }

        #region Supervisor Methods

        /// <summary>
        /// Adds a FileList to supervise list of this FileListSupervisor.
        /// </summary>
        /// <param name="fileList">A FileList control.</param>
        public void Register(IFileList fileList)
        {
            if (!_fileLists.Exists(afileList => afileList == fileList))
            {
                fileList.SelectedChanged += FileList_SelectedChanged;
                fileList.PathCleared += FileList_PathCleared;
                fileList.PathAdded += FileList_PathAdded;
                fileList.PathsAdded += FileList_PathsAdded;
                fileList.PathInserted += FileList_PathInserted;
                fileList.PathRemoved += FileList_PathRemoved;
                fileList.IndexRemoved += FileList_IndexRemoved;
                fileList.Renamed += FileList_Renamed;

                _fileLists.Add(fileList);
            }
        }

        /// <summary>
        /// Removes a FileList from supervise list of this FileListSupervisor.
        /// </summary>
        /// <param name="fileList">A FileList control.</param>
        public void Disregister(IFileList fileList)
        {
            if (_fileLists.Exists(afileList => afileList == fileList))
            {
                fileList.SelectedChanged -= FileList_SelectedChanged;
                fileList.PathCleared -= FileList_PathCleared;
                fileList.PathAdded -= FileList_PathAdded;
                fileList.PathsAdded -= FileList_PathsAdded;
                fileList.PathInserted -= FileList_PathInserted;
                fileList.PathRemoved -= FileList_PathRemoved;
                fileList.IndexRemoved -= FileList_IndexRemoved;
                fileList.Renamed -= FileList_Renamed;

                _fileLists.Remove(fileList);
            }
        }

        /// <summary>
        /// Performs the specified action on each FileList.
        /// </summary>
        /// <param name="action">The delegate to perform on each FileList.</param>
        public void ForEach(Action<IFileList> action)
        {
            foreach (IFileList aFileList in _fileLists)
            {
                action(aFileList);
            }
        }

        #endregion

        #region IFileList Methods

        public void SelectPrevious()
        {
            IsSupervising = true;
            foreach (IFileList aFileList in _fileLists)
            {
                if (aFileList == Supervisee)
                    continue;
                aFileList.SelectPrevious();
            }
            IsSupervising = false;
            OnSelectedChanged(EventArgs.Empty);
        }

        public void SelectNext()
        {
            IsSupervising = true;
            foreach (IFileList aFileList in _fileLists)
            {
                if (aFileList == Supervisee)
                    continue;
                aFileList.SelectNext();
            }
            IsSupervising = false;
            OnSelectedChanged(EventArgs.Empty);
        }

        public void SelectNone()
        {
            IsSupervising = true;
            foreach (IFileList aFileList in _fileLists)
            {
                if (aFileList == Supervisee)
                    continue;
                aFileList.SelectNone();
            }
            IsSupervising = false;
            OnSelectedChanged(EventArgs.Empty);
        }

        public void SelectAll()
        {
            IsSupervising = true;
            foreach (IFileList aFileList in _fileLists)
            {
                if (aFileList == Supervisee)
                    continue;
                aFileList.SelectAll();
            }
            IsSupervising = false;
            OnSelectedChanged(EventArgs.Empty);
        }

        public void SelectInvert()
        {
            IsSupervising = true;
            foreach (IFileList aFileList in _fileLists)
            {
                if (aFileList == Supervisee)
                    continue;
                aFileList.SelectInvert();
            }
            IsSupervising = false;
            OnSelectedChanged(EventArgs.Empty);
        }

        public int GetCount()
        {
            if (_fileLists.Count > 0)
                return _fileLists[0].Count;
            else
                return -1;
        }

        public string[] GetPaths()
        {
            if (_fileLists.Count > 0)
                return _fileLists[0].Paths;
            else
                return null;
        }

        public int GetSelectedIndex()
        {
            if (_fileLists.Count > 0)
                return _fileLists[0].GetSelectedIndex();
            else
                return -1;
        }

        public int[] GetSelectedIndices()
        {
            if (_fileLists.Count > 0)
                return _fileLists[0].GetSelectedIndices();
            else
                return null;
        }

        public string GetSelectedPath()
        {
            if (_fileLists.Count > 0)
                return _fileLists[0].GetSelectedPath();
            else
                return null;
        }

        public string[] GetSelectedPaths()
        {
            if (_fileLists.Count > 0)
                return _fileLists[0].GetSelectedPaths();
            else
                return null;
        }

        public void SetSelectedIndex(int fileIndex)
        {
            IsSupervising = true;
            foreach (IFileList aFileList in _fileLists)
            {
                if (aFileList == Supervisee)
                    continue;
                aFileList.SetSelectedIndex(fileIndex);
            }
            IsSupervising = false;
            OnSelectedChanged(EventArgs.Empty);
        }

        public void SetSelectedIndices(int[] fileIndices)
        {
            IsSupervising = true;
            foreach (IFileList aFileList in _fileLists)
            {
                if (aFileList == Supervisee)
                    continue;
                aFileList.SetSelectedIndices(fileIndices);
            }
            IsSupervising = false;
            OnSelectedChanged(EventArgs.Empty);
        }

        public void SetSelectedPath(string filePath)
        {
            IsSupervising = true;
            foreach (IFileList aFileList in _fileLists)
            {
                if (aFileList == Supervisee)
                    continue;
                aFileList.SetSelectedPath(filePath);
            }
            IsSupervising = false;
            OnSelectedChanged(EventArgs.Empty);
        }

        public void SetSelectedPaths(string[] filePaths)
        {
            IsSupervising = true;
            foreach (IFileList aFileList in _fileLists)
            {
                if (aFileList == Supervisee)
                    continue;
                aFileList.SetSelectedPaths(filePaths);
            }
            IsSupervising = false;
            OnSelectedChanged(EventArgs.Empty);
        }

        public string GetPathAt(int index)
        {
            if (_fileLists.Count > 0)
                return _fileLists[0].GetPathAt(index);
            else
                return null;
        }

        public int IndexOf(string filePath)
        {
            if (_fileLists.Count > 0)
                return _fileLists[0].IndexOf(filePath);
            else
                return -1;
        }

        public bool Contains(string filePath)
        {
            if (_fileLists.Count > 0)
                return _fileLists[0].Contains(filePath);
            else
                return false;
        }

        public void Clear()
        {
            IsSupervising = true;
            foreach (IFileList aFileList in _fileLists)
            {
                if (aFileList == Supervisee)
                    continue;
                aFileList.Clear();
            }
            IsSupervising = false;
            OnPathCleared(EventArgs.Empty);
        }

        public int Add(string filePath)
        {
            int theIndex = -1;
            if (filePath != null && !Contains(filePath))
            {
                IsSupervising = true;
                foreach (IFileList aFileList in _fileLists)
                {
                    if (aFileList == Supervisee)
                        continue;
                    theIndex = aFileList.Add(filePath);
                }
                IsSupervising = false;
                OnPathAdded(new PathEventArgs(filePath));
            }
            return theIndex;
        }

        public void AddRange(IEnumerable<string> filePaths)
        {
            IsSupervising = true;
            List<string> addedPathList = new List<string>();
            foreach (string aPath in filePaths)
            {
                if (aPath != null && !Contains(aPath))
                {
                    addedPathList.Add(aPath);
                }
            }
            
            //System.Diagnostics.Debug.Print("Adding time:");
            foreach (IFileList aFileList in _fileLists)
            {
                if (aFileList == Supervisee)
                    continue;

                //System.Diagnostics.Debug.Print(aFileList.GetType().ToString());
                //DateTime addingTime = DateTime.Now;

                aFileList.AddRange(addedPathList);

                //DateTime addedTime = DateTime.Now;
                //System.Diagnostics.Debug.Print(addedTime.Subtract(addingTime).ToString());
            }
            IsSupervising = false;
            OnPathsAdded(new PathsEventArgs(addedPathList.ToArray()));
        }

        public int Insert(int index, string filePath)
        {

            int theIndex = -1;
            if ((index >= 0 && index < Count) && (filePath != null && !Contains(filePath)))
            {
                IsSupervising = true;
                foreach (IFileList aFileList in _fileLists)
                {
                    if (aFileList == Supervisee)
                        continue;
                    theIndex = aFileList.Insert(index, filePath);
                }
                IsSupervising = false;
                OnPathInserted(new PathInsertedEventArgs(index, filePath));
            }
            return theIndex;
        }

        public int Remove(string filePath)
        {
            int theIndex = IndexOf(filePath);
            if (theIndex != -1)
            {
                IsSupervising = true;
                foreach (IFileList aFileList in _fileLists)
                {
                    if (aFileList == Supervisee)
                        continue;
                    theIndex = aFileList.Remove(filePath);
                }
                IsSupervising = false;
                OnPathRemoved(new PathEventArgs(filePath));
            }
            return theIndex;
        }

        public int RemoveAt(int index)
        {
            int theIndex = -1;
            if (index >= 0 && index < Count)
            {
                IsSupervising = true;
                foreach (IFileList aFileList in _fileLists)
                {
                    if (aFileList == Supervisee)
                        continue;
                    theIndex = aFileList.RemoveAt(index);
                }
                IsSupervising = false;
                OnIndexRemoved(new IndexEventArgs(index));
            }
            return theIndex;
        }

        public int Rename(string srcPath, string dstPath)
        {
            int theIndex = -1;
            if (dstPath != null && !Contains(dstPath))
            {
                theIndex = IndexOf(srcPath);
                if (theIndex != -1)
                {
                    IsSupervising = true;
                    foreach (IFileList aFileList in _fileLists)
                    {
                        if (aFileList == Supervisee)
                            continue;
                        theIndex = aFileList.Rename(srcPath, dstPath);
                    }
                    IsSupervising = false;
                    OnRenamed(new RenamedEventArgs(srcPath, dstPath));
                }
            }
            return theIndex;
        }

        public void EnsureVisible(int index)
        {
            IsSupervising = true;
            foreach (IFileList aFileList in _fileLists)
            {
                if (aFileList == Supervisee)
                    continue;
                aFileList.EnsureVisible(index);
            }
            IsSupervising = false;
        }

        public void EnsureVisible(string path)
        {
            IsSupervising = true;
            foreach (IFileList aFileList in _fileLists)
            {
                if (aFileList == Supervisee)
                    continue;
                aFileList.EnsureVisible(path);
            }
            IsSupervising = false;
        }

        #endregion

        #region IFileList Event Triggers

        protected void OnSelectedChanged(EventArgs e)
        {
            if (SelectedChanged != null)
                SelectedChanged(this, e);
        }

        protected void OnPathCleared(EventArgs e)
        {
            if (PathCleared != null)
                PathCleared(this, e);
        }

        protected void OnPathAdded(PathEventArgs e)
        {
            if (PathAdded != null)
                PathAdded(this, e);
        }

        protected void OnPathsAdded(PathsEventArgs e)
        {
            if (PathsAdded != null)
                PathsAdded(this, e);
        }

        protected void OnPathInserted(PathInsertedEventArgs e)
        {
            if (PathInserted != null)
                PathInserted(this, e);
        }

        protected void OnPathRemoved(PathEventArgs e)
        {
            if (PathRemoved != null)
                PathRemoved(this, e);
        }

        protected void OnIndexRemoved(IndexEventArgs e)
        {
            if (IndexRemoved != null)
                IndexRemoved(this, e);
        }

        protected void OnRenamed(RenamedEventArgs e)
        {
            if (Renamed != null)
                Renamed(this, e);
        }

        #endregion

        #region IFileList Event Handler Methods

        private void FileList_SelectedChanged(object sender, EventArgs e)
        {
            if (IsSupervising)
                return;
            Supervisee = sender as IFileList;
            int[] selectedIndices = Supervisee.SelectedIndices;
            SelectedIndices = selectedIndices;
            if (selectedIndices.Length > 0)
            {
                EnsureVisible(selectedIndices[selectedIndices.Length - 1]);
            }            
            Supervisee = null;
        }

        private void FileList_PathCleared(object sender, EventArgs e)
        {
            if (IsSupervising)
                return;
            Supervisee = sender as IFileList;
            Clear();
            Supervisee = null;
        }

        private void FileList_PathAdded(object sender, PathEventArgs e)
        {
            if (IsSupervising)
                return;
            Supervisee = sender as IFileList;
            Add(e.Path);
            Supervisee = null;
        }

        private void FileList_PathsAdded(object sender, PathsEventArgs e)
        {
            if (IsSupervising)
                return;
            Supervisee = sender as IFileList;
            AddRange(e.Paths);
            Supervisee = null;
        }

        private void FileList_PathInserted(object sender, PathInsertedEventArgs e)
        {
            if (IsSupervising)
                return;
            Supervisee = sender as IFileList;
            Insert(e.Index, e.Path);
            Supervisee = null;
        }

        private void FileList_PathRemoved(object sender, PathEventArgs e)
        {
            if (IsSupervising)
                return;
            Supervisee = sender as IFileList;
            Remove(e.Path);
            Supervisee = null;
        }

        private void FileList_IndexRemoved(object sender, IndexEventArgs e)
        {
            if (IsSupervising)
                return;
            Supervisee = sender as IFileList;
            RemoveAt(e.Index);
            Supervisee = null;
        }

        private void FileList_Renamed(object sender, RenamedEventArgs e)
        {
            if (IsSupervising)
                return;
            Supervisee = sender as IFileList;
            Rename(e.SourcePath, e.DestinationPath);
            Supervisee = null;
        }

        #endregion

        #region IFileList Properties

        public int Count
        {
            get { return GetCount(); }
        }

        public string[] Paths
        {
            get { return GetPaths(); }
        }

        public int SelectedIndex
        {
            get 
            {
                if (_fileLists.Count > 0)
                    return _fileLists[0].SelectedIndex;
                else
                     return -1;
            }
            set 
            {
                IsSupervising = true;
                foreach (IFileList aFileList in _fileLists)
                {
                    if (aFileList == Supervisee)
                        continue;
                    aFileList.SelectedIndex = value;
                }
                IsSupervising = false;
                OnSelectedChanged(EventArgs.Empty);
            }
        }

        public int[] SelectedIndices
        {
            get
            {
                if (_fileLists.Count > 0)
                    return _fileLists[0].SelectedIndices;
                else
                    return null;
            }
            set
            {
                IsSupervising = true;
                foreach (IFileList aFileList in _fileLists)
                {
                    if (aFileList == Supervisee)
                        continue;
                    aFileList.SelectedIndices = value;
                }
                IsSupervising = false;
                OnSelectedChanged(EventArgs.Empty);
            }
        }

        public string SelectedPath
        {
            get
            {
                if (_fileLists.Count > 0)
                    return _fileLists[0].SelectedPath;
                else
                    return null;
            }
            set
            {
                IsSupervising = true;
                foreach (IFileList aFileList in _fileLists)
                {
                    if (aFileList == Supervisee)
                        continue;
                    aFileList.SelectedPath = value;
                }
                IsSupervising = false;
                OnSelectedChanged(EventArgs.Empty);
            }
        }

        public string[] SelectedPaths
        {
            get
            {
                if (_fileLists.Count > 0)
                    return _fileLists[0].SelectedPaths;
                else
                    return null;
            }
            set
            {
                IsSupervising = true;
                foreach (IFileList aFileList in _fileLists)
                {
                    if (aFileList == Supervisee)
                        continue;
                    aFileList.SelectedPaths = value;
                }
                IsSupervising = false;
                OnSelectedChanged(EventArgs.Empty);
            }
        }

        public string this[int index]
        {
            get { return GetPathAt(index); }
        }

        #endregion

        /// <summary>
        /// Gets or sets the IFlieList object which trigged some public event.
        /// </summary>
        protected IFileList Supervisee
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets a value indicating whether the FileListSupervisor is
        /// operating IFileList objects and all public event should be ignored.
        /// </summary>
        protected bool IsSupervising
        {
            get;
            set;
        }

        #region IFileList Events

        public event EventHandler SelectedChanged;

        public event EventHandler PathCleared;

        public event PathEventHandler PathAdded;

        public event PathsEventHandler PathsAdded;

        public event PathInsertedEventHandler PathInserted;

        public event PathEventHandler PathRemoved;

        public event IndexEventHandler IndexRemoved;

        public event RenamedEventHandler Renamed;

        #endregion
    }
}

/*


                fileSystemWatcher.FilePath = _folderPath;
                fileSystemWatcher.EnableRaisingEvents = true;

                fileSystemWatcher.FilePath = String.Empty;
                fileSystemWatcher.EnableRaisingEvents = false;

*/